<?php
declare(strict_types=1);
namespace App\model\Tencrypt;
use App\Exception\Handler\AppExceptionHandler;
use Hyperf\HttpMessage\Exception\HttpException;
use Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler;

/*****
 * @desc 加密类
 * @author daming
 * */
class Tencrypt{

    private static $instance;
    private static $cipher_algo; //加密算法
    private static $hash_algo;   //加密摘要
    private static $iv_num_bytes; //密码长度
    private static $format;
    const FORMAT_B64 = 1;
    const FORMAT_HEX = 2;

    public function __construct($cipher_algo = 'aes-256-ctr', $hash_algo = 'sha256', $fmt = Tencrypt::FORMAT_HEX)
    {
        self::$cipher_algo = $cipher_algo;
        self::$hash_algo = $hash_algo;
        self::$format = $fmt;
        //判断加密算法是否符合规则存在
        if (!in_array($cipher_algo,openssl_get_cipher_methods(true))){
            //echo "error"; //这里后续需要抛出异常
           throw new HttpException(500,'error');
        }

        //判断摘要算法是否可行
        if (!in_array($hash_algo,openssl_get_md_methods(true))){
            throw new HttpException(500,'error1');
        }

        //获取密码长度
        self::$iv_num_bytes = openssl_cipher_iv_length($cipher_algo);
    }
    /**
     * static pool
     * @return Tencrypt
     * @throws \Exception
     */
    public static function getInstance()
    {
        if (!isset(self::$instance)) {
            self::$instance = new static();
        }
        return self::$instance;
    }

    //加密
    public  function Encrypt($in, $key, $fmt = null){
        if (empty($fmt)){
            $fmt = 2;
        }
        //生成随机key 追加用
        $iv = openssl_random_pseudo_bytes(self::$iv_num_bytes, $isStrongCrypto);
        if (!$isStrongCrypto) {
           // throw new \Exception("Encrypt - echo fail");
            throw new HttpException(500,'error');

        }

        // hhash
        $keyhash = openssl_digest($key, self::$hash_algo, true);

        // 生成密文 encrypt
        $opts = OPENSSL_RAW_DATA;
        $encrypted = openssl_encrypt($in, self::$cipher_algo, $keyhash, $opts, $iv);

        if ($encrypted === false) {
           // throw new \Exception('Encrypt -  error: ' . openssl_error_string());
            throw new HttpException(500,'error');
        }

        // 拼接
        $res = $iv . $encrypted;

        // 处理
        if ($fmt == Tencrypt::FORMAT_B64) {
            $res = base64_encode($res);
        } else if ($fmt == Tencrypt::FORMAT_HEX) {
            $res = unpack('H*', $res)[1];
        }

        return $res;
    }

    /**
     * 解密一个字符串
     * @param  string $in 要解密的字符串
     * @param  string $key 密钥
     * @param  int $fmt data处理格式  1 base 64  2 二进制
     * @return string      解开的string
     * @throws \Exception
     */
    public function Decrypt($in, $key, $fmt = null)
    {
        if ($fmt === null) {
            $fmt = self::$format;
        }

        $raw = $in;

        // 处理字符串
        if ($fmt == Tencrypt::FORMAT_B64) {
            $raw = base64_decode($in);
        } else if ($fmt == Tencrypt::FORMAT_HEX) {
            $raw = pack('H*', $in);
        }

        //如果长度比随机key长度还短
        if (strlen($raw) < self::$iv_num_bytes) {
           // throw new \Exception('Decrypt - ' . 'data len ->' . strlen($raw) . "   iv len -> ".self::iv_num_bytes);
            throw new HttpException(500,'error');
        }

        // 获取添加的随机key,和真正的密文
        $iv = substr($raw, 0, self::$iv_num_bytes);
        $raw = substr($raw, self::$iv_num_bytes);

        // hash
        $keyhash = openssl_digest($key, self::$hash_algo, true);

        // 解密
        $opts = OPENSSL_RAW_DATA;
        $res = openssl_decrypt($raw, self::$cipher_algo, $keyhash, $opts, $iv);

        if ($res === false) {
            //throw new \Exception('Encrypt -  error: ' . openssl_error_string());

            throw new HttpException(500,'error');
        }

        return $res;
    }







}